using System;
using LoA.Shared.Interfaces;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace LoA.ObjectEngine.Machines {
	/// <summary>
	/// Helper class for drawing a tank model with animated wheels and turret.
	/// </summary>
	public class Tank : IDrawable {
		public Tank(Game game, string Model, ICamera camera) {
			this.modelPath = Model;
			this.game = game;
			//this.LoadContent( );
			this.Camera = camera;
		}

		ICamera Camera;

		// The XNA framework helpers to display anything.
		readonly Game game;
		public Game Game { get { return this.game; } }
		public GraphicsDevice GraphicsDevice { get { return game.GraphicsDevice; } }

		// The XNA framework Model object that we are going to display.
		readonly string modelPath;
		Model tankModel;

		// Shortcut references to the bones that we are going to animate.
		// We could just look these up inside the Draw method, but it is more
		// efficient to do the lookups while loading and cache the results.
		ModelBone leftBackWheelBone;
		ModelBone rightBackWheelBone;
		ModelBone leftFrontWheelBone;
		ModelBone rightFrontWheelBone;
		ModelBone leftSteerBone;
		ModelBone rightSteerBone;
		ModelBone turretBone;
		ModelBone cannonBone;
		ModelBone hatchBone;

		// Store the original transform matrix for each animating bone.
		Matrix leftBackWheelTransform;
		Matrix rightBackWheelTransform;
		Matrix leftFrontWheelTransform;
		Matrix rightFrontWheelTransform;
		Matrix leftSteerTransform;
		Matrix rightSteerTransform;
		Matrix turretTransform;
		Matrix cannonTransform;
		Matrix hatchTransform;

		// Array holding all the bone transform matrices for the entire model.
		// We could just allocate this locally inside the Draw method, but it
		// is more efficient to reuse a single array, as this avoids creating
		// unnecessary garbage.
		Matrix[ ] boneTransforms;

		// Current model positions.
		Vector3 position;
		Vector3 direction;
		Vector3 up;

		/// <summary>
		/// 
		/// </summary>
		public Vector3 Position { get { return this.position; } set { this.position = value; } }

		/// <summary>
		/// 
		/// </summary>
		public Vector3 Direction { get { return this.direction; } set { this.direction = value; } }

		/// <summary>
		/// 
		/// </summary>
		public Vector3 Up { get { return this.up; } set { this.up = value; } }

		/// <summary>
		/// 
		/// </summary>
		public Matrix World { get { return Matrix.CreateWorld(this.position, this.direction, this.up); } set { this.position = value.Translation; this.direction = value.Forward; this.up = value.Up; } }

		// Current animation positions.
		float wheelRotationValue;
		float steerRotationValue;
		float turretRotationValue;
		float cannonRotationValue;
		float hatchRotationValue;

		/// <summary>
		/// Gets or sets the wheel rotation amount.
		/// </summary>
		public float WheelRotation {
			get { return wheelRotationValue; }
			set { wheelRotationValue = value; }
		}

		/// <summary>
		/// Gets or sets the steering rotation amount.
		/// </summary>
		public float SteerRotation {
			get { return steerRotationValue; }
			set { steerRotationValue = value; }
		}

		/// <summary>
		/// Gets or sets the turret rotation amount.
		/// </summary>
		public float TurretRotation {
			get { return turretRotationValue; }
			set { turretRotationValue = value; }
		}

		/// <summary>
		/// Gets or sets the cannon rotation amount.
		/// </summary>
		public float CannonRotation {
			get { return cannonRotationValue; }
			set { cannonRotationValue = value; }
		}

		/// <summary>
		/// Gets or sets the entry hatch rotation amount.
		/// </summary>
		public float HatchRotation {
			get { return hatchRotationValue; }
			set { hatchRotationValue = value; }
		}

		protected void LoadContent( ) {
			// Load the tank model from the ContentManager.
			tankModel = this.Game.Content.Load<Model>(modelPath);

			// Look up shortcut references to the bones we are going to animate.
			leftBackWheelBone = tankModel.Bones["l_back_wheel_geo"];
			rightBackWheelBone = tankModel.Bones["r_back_wheel_geo"];
			leftFrontWheelBone = tankModel.Bones["l_front_wheel_geo"];
			rightFrontWheelBone = tankModel.Bones["r_front_wheel_geo"];
			leftSteerBone = tankModel.Bones["l_steer_geo"];
			rightSteerBone = tankModel.Bones["r_steer_geo"];
			turretBone = tankModel.Bones["turret_geo"];
			cannonBone = tankModel.Bones["canon_geo"];
			hatchBone = tankModel.Bones["hatch_geo"];

			// Store the original transform matrix for each animating bone.
			leftBackWheelTransform = leftBackWheelBone.Transform;
			rightBackWheelTransform = rightBackWheelBone.Transform;
			leftFrontWheelTransform = leftFrontWheelBone.Transform;
			rightFrontWheelTransform = rightFrontWheelBone.Transform;
			leftSteerTransform = leftSteerBone.Transform;
			rightSteerTransform = rightSteerBone.Transform;
			turretTransform = turretBone.Transform;
			cannonTransform = cannonBone.Transform;
			hatchTransform = hatchBone.Transform;

			// Allocate the transform matrix array.
			boneTransforms = new Matrix[tankModel.Bones.Count];
		}

		/// <summary>
		/// Draws the tank model, using the current animation settings.
		/// </summary>
		public void Draw(GameTime gameTime) {
			// Set the world matrix as the root transform of the model.
			tankModel.Root.Transform = World;

			// Calculate matrices based on the current animation position.
			Matrix wheelRotation = Matrix.CreateRotationX(wheelRotationValue);
			Matrix steerRotation = Matrix.CreateRotationY(steerRotationValue);
			Matrix turretRotation = Matrix.CreateRotationY(turretRotationValue);
			Matrix cannonRotation = Matrix.CreateRotationX(cannonRotationValue);
			Matrix hatchRotation = Matrix.CreateRotationX(hatchRotationValue);

			// Apply matrices to the relevant bones.
			leftBackWheelBone.Transform = wheelRotation * leftBackWheelTransform;
			rightBackWheelBone.Transform = wheelRotation * rightBackWheelTransform;
			leftFrontWheelBone.Transform = wheelRotation * leftFrontWheelTransform;
			rightFrontWheelBone.Transform = wheelRotation * rightFrontWheelTransform;
			leftSteerBone.Transform = steerRotation * leftSteerTransform;
			rightSteerBone.Transform = steerRotation * rightSteerTransform;
			turretBone.Transform = turretRotation * turretTransform;
			cannonBone.Transform = cannonRotation * cannonTransform;
			hatchBone.Transform = hatchRotation * hatchTransform;

			// Look up combined bone matrices for the entire model.
			tankModel.CopyAbsoluteBoneTransformsTo(boneTransforms);

			// Draw the model.
			foreach (ModelMesh mesh in tankModel.Meshes) {
				foreach (BasicEffect effect in mesh.Effects) {
					effect.World = boneTransforms[mesh.ParentBone.Index];
					effect.View = Camera.ViewMatrix;
					effect.Projection = Camera.ViewMatrix;

					effect.EnableDefaultLighting( );
				}

				mesh.Draw( );
			}
		}

		public int DrawOrder { get; set; }

		public event EventHandler<EventArgs> DrawOrderChanged;

		public bool Visible { get; set; }

		public event EventHandler<EventArgs> VisibleChanged;
	}
}
